/*
* Copyright 2011 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/
package org.etherframework.core.common
{
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.utils.Dictionary;
	
	import mx.utils.UIDUtil;
	
	import org.etherframework.IEther;
	import org.etherframework.common.IDestroyable;
	import org.etherframework.common.IPersistent;
	import org.etherframework.common.command.facade.ICommands;
	import org.etherframework.common.dependency.facade.IDependencies;
	import org.etherframework.common.mediation.facade.IMediations;
	import org.etherframework.common.mediators.facade.IMediators;
	import org.etherframework.common.mediators.facade.IMediatorsFacade;
	import org.etherframework.core.bridge.IBridge;
	import org.etherframework.core.bridge.IBridgeFacades;
	import org.etherframework.core.bridge.IInteraction;
	import org.etherframework.core.context.Context;
	import org.etherframework.core.context.IContextInterceptor;
	import org.etherframework.core.context.IContexts;
	import org.etherframework.core.context.IContextual;
	import org.etherframework.core.context.IContextualCreator;
	import org.etherframework.core.context.IPersistentContextual;
	
	use namespace ether_internal;
	
	[ExcludeClass]
	public final class Ether implements IEther, IContextualCreator, 
		IPersistentContextual, IContextInterceptor, IDestroyableNotifier
	{
		private var _contexts:IContexts;
		private var _bridge:IBridge;
		private var _facade:IBridgeFacades;
		private var _interaction:IInteraction;
		private var _pendingListeners:Vector.<Object>;
		private var _mediatorsFacade:IMediatorsFacade;
		private var _destroyHandlers:Dictionary;
		private var _uid:String;
		
		ether_internal var _context:Context;
		ether_internal var _target:Object;
		
		ether_internal final function __init(target:Object):void
		{
			_target = target;
		}
		
		public function Ether(bridge:IBridge, context:Context)
		{
			_bridge = bridge;
			_context = context;
			_facade = bridge.facade.contextual(context);
			_interaction = _bridge.interaction;
			_contexts = _bridge.contexts;
		}
		
		public function dispatch(event:Event):void
		{
			_interaction.dispatchEvent(this, event);
		}
		
		public function listen(type:String, listener:Function):void
		{
			if (!_target)
			{
				if (!_pendingListeners)
					_pendingListeners = new Vector.<Object>();
				
				_pendingListeners.push(type, listener);
				return;
			}
			
			if (!(_target is IPersistent))
				throw new IllegalOperationError("Object " + _target + 
					" is not persisted.");
			
			_interaction.listenEvent(this, type, listener);
		}
		
		public function unlisten(type:String, listener:Function):void
		{
			_interaction.unlistenEvent(this, type, listener);
		}
		
		public function unlistenAll():void
		{
			_interaction.unlistenAll(this);
		}
		
		public function destroy():void
		{
			if (_target && (_target is IDestroyable))
			{
				_bridge.destruct(_target as IDestroyable);
			}
			else
			{
				destroyed();
			}
		}
		
		public function contextual(context:Context):IEther
		{
			return _contexts.contextual(context, IEther, this) as IEther;
		}
		
		public function create(context:Context, type:Class):IContextual
		{
			return _bridge.createEther(context);
		}
		
		public function dispose():void
		{
			_interaction.unlistenAll(this);
		}
		
		public function intercept(action:Function):void
		{
			if (!(action is Function))
				throw new ArgumentError("Argument action:Function can not be null.");
			
			_interaction.retainContext(this);
			action();
			_interaction.releaseContext();
		}
		
		public function addHandler(handler:Function):void
		{
			if (!_destroyHandlers)
				_destroyHandlers = new Dictionary(true);
			
			if (!_destroyHandlers[handler])
			{
				_destroyHandlers[handler] = true;
			}
		}
		
		public function removeHandler(handler:Function):void
		{
			if (_destroyHandlers && _destroyHandlers[handler])
			{
				delete _destroyHandlers[handler];
			}
		}
		
		public function get context():Context { return _context; }
		public function get uid():String { return _uid || (_uid = UIDUtil.createUID()); }
		
		ether_internal function processed():void
		{
			if (_pendingListeners && _target)
			{
				if (_target is IPersistent)
				{
					const len:int = _pendingListeners.length;
					const pendings:Vector.<Object> = _pendingListeners;
					for (var i:int = 0; i < len; ++i)
					{
						_interaction.listenEvent(this, 
							pendings[i] as String, 
							pendings[++i] as Function); 
					}
				}
				_pendingListeners = null;
			}
		}
		
		ether_internal function destroyed():void
		{
			_contexts.dispose(this);
			
			if (_destroyHandlers)
			{
				for each (var handler:Function in _destroyHandlers) 
				{
					handler(this);
				}
				_destroyHandlers = null;
			}
		}
		
		public function get dependencies():IDependencies { return _facade.dependencies.prepare(this) as IDependencies; }
		public function get mediators():IMediators { return _facade.mediators.prepare(this) as IMediators; }
		public function get commands():ICommands { return _facade.commands.prepare(this) as ICommands; }
		public function get mediations():IMediations { return _facade.mediations.prepare(this) as IMediations; }
	}
}